/**
 * @file schedule.c
 * @synopsis  调度otdr通道使用spi进行测量
 * @author wen wjcapple@163.com
 * @version 2.0
 * @date 2016-06-24
 */
#include <semaphore.h>

#include "otdr_ch.h"
#include "hb_app.h"
#include "global.h"
#include "adpt_otdr.h"
#include "adpt_alarm.h"
//定义otdr通道资源
struct _tagOtdrDev *otdrDev;
//用户点名测量，配置测量
struct _tagUsrOtdrTest usrOtdrTest;
//算法运行时当前的通道信息,开始测量的时候赋值，算法运行完毕清空
//spi 读取数据的缓冲区 spi 数据格式： fe 4B 2B fe 4B 2B
#define SPI_OP_SPACE_MS		1000 //spi设备的操作间隔  
sem_t sem_UsrTest;
pthread_mutex_t mutex_measur;
_tagSpiDevCtrl spi_ctrl;
struct fpga_dev *fpga_device;
//记录线程信息
struct _tagThreadInfo tsk_debug_info;
struct _tagThreadInfo tsk_measur_info;
struct _tagThreadInfo tsk_service_info;
struct _tagCHAlarmDis chAlarmDis;
int32_t tsk_debug(void *arg)
{
	struct _tagThreadInfo *pthread_info;
	pthread_info = (struct _tagThreadInfo *)arg;
	pthread_info->top_id = (long int)syscall(224);

	printf("%s %s  pid %d  self_id 0x%x \n",__FILENAME__, __FUNCTION__, (long int)syscall(224),(int)pthread_self());

	while(1){
		ret_total_curalarm2host();
		printf("%s %d sleep \n", __FUNCTION__, __LINE__);
		//usr_delay(0, SPI_OP_SPACE_MS);
		printf("%s %d get up \n", __FUNCTION__, __LINE__);
	}
	pthread_exit(0);
}

extern struct _tagIpSwitchCtr IPCtrl;
int32_t check_net_falg(struct ep_t *pep, char dst_ip[])
{
	int32_t net_sec, fd;
	read_net_flag();
	if(IPCtrl.re_connect){
		IPCtrl.re_connect = 0;
		net_sec = IPCtrl.flag ^ 1;
		snprintf(dst_ip,16 , "192.168.%d.200",net_sec);
		fd =  tms_SelectFdByAddr(ADDR_CU);

		if(fd > 0)
			ep_Close(pep,NULL,fd);
		
	}
	return 0;
}
struct _tagTickCount TickCount;
int32_t check_connect2cu(char dst_ip[],int32_t *pstate)
{
	struct ep_con_t  epnode;
	struct glink_addr addr;
	int32_t dst_port = 6000, net_sec, fd;
	int32_t state, ret;
	struct tms_context *pcontext;
	static int32_t reconnect_num = 0, reconnect_count = 0;
	static int32_t count = 0, report_count = 0;;
	addr.dst = ADDR_CU;
	addr.src = ADDR_LOCAL;
	addr.pkid = get_pkid();
	//检查CU是否失去连接
	fd =  tms_SelectFdByAddr(ADDR_CU);
	count++;
	/*
	 *第一次重连的间隔是0.5秒，第n次重连的间隔是n*0.5
	 */
	state = 1;
	if(fd < 0 && reconnect_count >= reconnect_num){
		epnode.sockfd = -1;
		reconnect_count = 0;
		PR_X("connect dst ip %s port %d \n",dst_ip, dst_port);
		ep_Connect(&ep_client, &epnode, dst_ip,dst_port);
		if(epnode.sockfd != -1)
		{
			report_count = 0;
			//RetLocalCardType(&(((struct tmsxx_app*)(epnode.ptr))->context));
			tms_RefreshAddrFd(epnode.sockfd, ADDR_CU);
			reconnect_num = 0;
			reconnect_count = 0;
			write_gpio_val(gpio_fd.alarm_total_1,0);
			state = 0;
			TickCount.src_e = 0;
		}
		else if(reconnect_num >= 20)
			reconnect_num = 20;
		else 
			reconnect_num++;
	}
	else if(fd < 0)
		reconnect_count++;
		
	else
       	{
		
		reconnect_num = 0;
		reconnect_count = 0;
		state = 0;
		/*
		 * 2017-10-10 为了防止发送错误，那么就发送五次
		*/
		if(report_count < 5)
		{
			ret = get_context_by_dst(ADDR_CU,&pcontext);
			if(ret == 0)
				RetLocalCardType(pcontext);
			else
				printf("%s %d can't report card type ret %d \n", __FUNCTION__,__LINE__,ret);
		}
		if(report_count == 6)
			ret_total_curalarm2host_V2(&addr);

		report_count++;
		if(count % 4 == 0)
		{
			TickCount.src_e++;
			tms_Tick(fd, &addr);
			if(TickCount.src_e >= 8)
			{
				ep_Close(&ep_client,NULL,fd);
				state = 1;
			}
		}
	}
	if(reconnect_num == 10)
		write_gpio_val(gpio_fd.alarm_total_1,1);
	*pstate = state;
	assert(reconnect_num <= 15);
	return 0;


}
/* --------------------------------------------------------------------------*/
/**
 * @synopsis  tsk_connect,检查网络连接以及重连
 *
 * @param arg
 *
 * @returns   
 */
/* ----------------------------------------------------------------------------*/
extern struct ep_t ep_client;
struct _tagThreadInfo tsk_connect_info;
struct _tagCuState cu_state;
int32_t tsk_connect(void *arg)
{
	struct _tagThreadInfo *pthread_info;
	//struct _tagCuState cu_state;
	int32_t delay_us = 500000;
	int32_t ret, count; 
	struct tms_context *pcontext;
	char dst_ip[16];
	int32_t net_sec;

	pthread_info = (struct _tagThreadInfo *)arg;
	pthread_info->top_id = (long int)syscall(224);

	PR_X("pid %d  self_id 0x%x \n",(long int)syscall(224),(int)pthread_self());
	memset(&cu_state, 0, sizeof(struct _tagCuState));
	count = 0;

	net_sec = IPCtrl.flag ^ 1;
	snprintf(dst_ip,16 , "192.168.%d.200",net_sec);

	while(1){

		//检查是否切换网段，如果切换网段，主动关掉连接
		if(count % 4 == 0)
			check_net_falg(&ep_client,dst_ip);


		//检查连接是否存在
		if(count % 2 == 0)
			check_connect2cu(dst_ip, &cu_state.state);

		count++;	
		usleep(delay_us);
	}
	pthread_exit(0);
}

extern unsigned int fpga_alive(struct fpga_dev *dev);
int32_t tsk_service(void *arg)
{
	struct _tagThreadInfo *pthread_info;
	int32_t delay_us = 500000;
	int32_t ret, count; 
	struct tms_context *pcontext;

	pthread_info = (struct _tagThreadInfo *)arg;
	pthread_info->top_id = (long int)syscall(224);

	PR_X("pid %d  self_id 0x%x \n",(long int)syscall(224),(int)pthread_self());


	count = 0;
	while(1){
		if(cu_state.state)
			delay_us = 2000000;
		else
			delay_us = 500000;


		if(count % 8 == 0 && fpga_device != NULL)
			fpga_alive(fpga_device);


		//运行灯闪烁
		run_led_blink();
		count++;	
		usleep(delay_us);
	}
	pthread_exit(0);
}

//存放算法曲线的缓冲区
float algorAi[DATA_LEN];
extern struct _tagOtdrTestCtrl otdrTestCtrl;
int32_t tsk_measur(void *arg)
{
	int32_t ch, ret;
	struct _tagCHPara usr_test_para;
	struct _tagCHPara *pch_para;
	struct _tagOtdrDev *potdr_dev;
	struct _tagCHFiberSec *pch_fiber_sec;
	struct _tagHostOtdrPara HostPara;
	struct _tagAlarmModuleAdpt *pAlarmAdpt;
	FiberAlarmInfo_t CurAlarm;
	struct _tagThreadInfo *pthread_info;
	struct glink_addr addr;
	struct tms_ack ack;
	memset(&ack, 0, sizeof(struct tms_ack));
	pthread_mutex_t *pmutex;
	OtdrTestResult_t ch_tr;
	OTDRCurveInfo_t cur_alarm_curv;
	OTDREvent_t ep_buf[MAX_EVENT_NUM];   //事件缓冲区    
	FiberSectionByEvent_t efs_buf[MAX_EVENT_NUM];     //光线段缓冲区
	int32_t is_switch,count,tmp, pasue_count;
	uint32_t pasue_num,pulse_used,stat_end, cat_pt;

	is_switch = 0;

	pmutex = &mutex_measur;
	pthread_info = (struct _tagThreadInfo *)arg;
	pthread_info->top_id = (long int)syscall(224);
	ch_tr.Ai = algorAi;
	printf("%s %s  pid %d  self_id 0x%x \n",__FILENAME__, __FUNCTION__, (long int)syscall(224),(int)pthread_self());
	//告警模块查找告警的时候使用的曲线
	cur_alarm_curv.testresult.eventinfo = ep_buf;
	cur_alarm_curv.testresult.sectioninfo = efs_buf;
	cur_alarm_curv.testresult.curvepointnum = DATA_LEN - 1000;
	cur_alarm_curv.testresult.curvepoint = algorAi;

	ch = 0;
	count = 0;
	pasue_count = 0;
	pasue_num = 0xABA95000; //10us计，8个小时后启动
	memset(&chAlarmDis, 0, sizeof(struct _tagCHAlarmDis));
	pulse_used = 0;
	while(1){
		pthread_mutex_lock(pmutex);

		pch_fiber_sec = &chFiberSec[ch];
		potdr_dev = &otdrDev[ch];
		is_switch = 0;

		//处理点名测量
		ret = sem_trywait(&sem_UsrTest);
		if(ret == 0)
		{
			if(usrOtdrTest.state == USR_OTDR_TEST_WAIT)
			{
				deal_appoint_test(&usrOtdrTest,&ch_tr,&spi_ctrl,cb_stop);
				goto go_on;
			}
		}
		//minishell里面可以暂停轮询监测 如果忘记重新开启，8小时候自动重新监测
		if(otdrTestCtrl.pause_cyc == 1)
		{
			pasue_count++;
			if(pasue_count > pasue_num)
				otdrTestCtrl.pause_cyc = 0;
			goto go_on;
		}
		else
			pasue_count = 0;
		
		ret = sem_trywait(&potdr_dev->sem_msg);
		//更新参数
		if(ret == 0 && potdr_dev->ch_ctrl.refresh_para)
		{
			refresh_ch_monitor_para(pch_fiber_sec, potdr_dev);
			set_ch_alarm_status(ch, 0,&chAlarmDis);
			otdrTestCtrl.monitor_num = refresh_monior_ch_num();
			//必须放到外面来发送告警，否则会死锁的
			if(otdrTestCtrl.monitor_num == 0){
				otdrTestCtrl.is_send_alarm = 1;
				ret_total_curalarm2host();
			}
		}

		//循环测量
		if(potdr_dev->ch_ctrl.is_cfged){
			pch_para = &otdrDev[ch].ch_para;
			//pch_para->PulseWidth_ns = adpt_test_pulse(&otdrDev[ch]);

			/*
			 * 第一次测试，使用标准脉宽，第二次根据测试结果修改脉宽，第三次仍然根据测试结果修改
			 * 如果前两次测试告警相同，那么不再进行第二次测试
			*/
			if(pulse_used == 0)
				pch_para->PulseWidth_ns = otdrDev[ch].ch_ctrl.st_pulse;
			else
				pch_para->PulseWidth_ns = pulse_used;
			pch_para->NonRelectThreshold = 0;

			ret = get_ch_otdr_curv(ch, pch_para,&ch_tr,&spi_ctrl,cb_stop);
			assert(ret == 0 || ret == 1);
			if(ret != 0)
				PR_X("get otdr curv error ret = %d \n",ret);
			else
			{
				otdrDev[ch].ch_ctrl.cur_fiber_len = ch_tr.FiberLen;
				otdrDev[ch].ch_ctrl.test_count++;
			}

			if(otdrTestCtrl.stop)
				goto go_on;

			get_cyc_curv_from_algor(&otdrDev[ch].curv, &ch_tr, ch + ch_offset);
			convert_curv_aglo2alarmmoudle(&ch_tr,&cur_alarm_curv);
			//防止这段时间光线段配置发生改变
			quick_lock(&pch_fiber_sec->lock);
			
			if(pch_fiber_sec->para.is_initialize){
				
				pAlarmAdpt = &pch_fiber_sec->adpt_moudle;
				//准备查找告警
				CurAlarm.fiberalarm = pAlarmAdpt->cur_alarm.fiberalarm;
				CurAlarm.alarmnum = pAlarmAdpt->cur_alarm.alarmnum;	
				ret = find_fiber_alarm(&pAlarmAdpt->std_curv, 
						&cur_alarm_curv, 
						&pAlarmAdpt->fiber_sec, //光纤段信息
						CurAlarm.alarmnum, 
						&CurAlarm,
						NULL);   //允许的最大告警数目
				assert(ret == 0);
				printf("ch %d pl %d test num %d \n", ch,pch_para->PulseWidth_ns,\
					       	pAlarmAdpt->alarm_cache.test_num);

				refresh_fiber_statistc(&ch_tr, pch_fiber_sec, ch + ch_offset);
				if(ret == 0)
				{
					ret = refresh_alarm_cache(&pAlarmAdpt->alarm_cache,&CurAlarm, &pch_fiber_sec->alarm);
					if(ret == SEND_ALARM_AND_SWITCH_CH)
						memcpy(pch_fiber_sec->alarm.pcurv, &potdr_dev->curv.curv, \
								sizeof(struct _tagUpOtdrCurv));
						
					//2017-09-14 告警级别为1，同时链长变短
					tmp = get_max_serious_alarm(&CurAlarm);
					printf("ch %d alarm_num %d lev %d fiber_len %0.3f 0.67linb %.3f\n",ch,CurAlarm.alarmnum,\
							tmp,ch_tr.FiberLen, potdr_dev->ch_ctrl.sec_loss.alarm_dis);
					if(ret == CONTINUE_TEST && CurAlarm.alarmnum > 0 &&\
						       	/*pulse_used == 0 &&*/ tmp == FIBER_ALARM_LEV1 && \
							ch_tr.FiberLen < potdr_dev->ch_ctrl.sec_loss.alarm_dis )
					{
						pulse_used = get_test_pulse_v1(potdr_dev, &ch_tr);
						printf("ch %d change to pl %d  \n",ch, pulse_used);
					}	
					else
						pulse_used = 0;


				}
				else 
					ret = SWITCH_CH;
			}
			quick_unlock(&pch_fiber_sec->lock);
			//判断是否切换通道
			if(ret != CONTINUE_TEST){
				is_switch = 1;
				count++;
			}
			//轮询第一个周期结束，无论是否告警是否变化，都需要上报
			//轮询第一个周期内，无论告警是否变化，都不能上报
			if( ( count == otdrTestCtrl.monitor_num )|| 
					( ret == SEND_ALARM_AND_SWITCH_CH && count > otdrTestCtrl.monitor_num )
			  ){
				otdrTestCtrl.is_send_alarm = 1;
				addr.dst = ADDR_HOST_NODE;
				addr.src = ADDR_LOCAL;
				addr.pkid = get_pkid();
				ret_total_curalarm2host();
			}
			//如果没有配置，灭灯。如果告警数目小于0也灭灯
			if(pch_fiber_sec->alarm.alarm_num > 0)
				set_ch_alarm_status(ch,1,&chAlarmDis);
			else
				set_ch_alarm_status(ch,0,&chAlarmDis);

		}
		else{
			is_switch = 1;
			set_ch_alarm_status(ch, 0, &chAlarmDis);
		}

go_on:
		if(is_switch){
			pulse_used = 0;
			is_switch = 0;
			ch++;
			ch = ch % CH_NUM;
		}
		pthread_mutex_unlock(pmutex);
		//如果用户停止，多睡一会
		if(otdrTestCtrl.stop)
			usleep(90000);
		else
			usleep(10);
	}



}

